package com.sg.fileService.service.impl;

import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import com.sg.common.exception.BaseBusinessException;
import com.sg.fileService.common.ErrorCode;
import com.sg.fileService.common.FileUploadException;
import com.sg.fileService.common.FileUploadTools;
import com.sg.fileService.dao.FileDataMapper;
import com.sg.fileService.entity.FailUploadReason;
import com.sg.fileService.entity.FileTypeConfig;
import com.sg.fileService.entity.UploadResult;
import com.sg.fileService.model.FileData;
import com.sg.fileService.model.FilePath;
import com.sg.fileService.service.IFileUploadService;
import com.sg.fileService.util.PathCreator;
import com.sg.fileService.config.Property;

/**
 * ClassName:FileUploadServiceImpl <br/>
 * Function: 文件上传服务实现 <br/>
 * Reason:   实现文件上传服务功能 <br/>
 * Date:     2017年12月26日 下午5:39:59 <br/>
 * Update：      20180224
 *
 * @author Lwt, xzk
 * @see
 * @since JDK 1.8
 */
@Slf4j
public abstract class BaseFileUploadServiceImpl implements IFileUploadService {

    @Autowired
    private FileDataMapper mapper;

    @Override
    public UploadResult uploadFile(HttpServletRequest request, String proPath) throws BaseBusinessException{
        //如果没有指定文件保存路径
        if (StringUtils.isEmpty(proPath)) {
            return uploadFileDefault(request);
        } else {
            return toUploadFile(request, proPath);
        }
    }

    @Override
    public UploadResult uploadFileDefault(HttpServletRequest request) throws BaseBusinessException{
        return toUploadFile(request, "default");
    }

    private UploadResult toUploadFile(HttpServletRequest request, String proPath) throws BaseBusinessException{
        //创建一个通用的多部分解析器
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());

        FileTypeConfig fileConfig = null;

        /**
         * 生成上传结果报告
         */
        UploadResult result = new UploadResult();
        //判断 request 是否有文件上传,即多部分请求  
        if (multipartResolver.isMultipart(request)) {
            //转换成多部分request
            MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
            //取得request中的所有文件名  
            Iterator<String> iter = multiRequest.getFileNames();
            while (iter.hasNext()) {
                //取得上传文件
                MultipartFile file = multiRequest.getFile(iter.next());

                //如果名称不为“”,说明该文件存在，否则说明该文件不存在  
                if (file != null && file.getOriginalFilename().trim() != "") {
                    //取得当前上传文件的文件名称  
                    String fileName = file.getOriginalFilename();
                    //获取文件配置
                    fileConfig = FileUploadTools.getTypeConfig0(fileName);
                    //生成文件路径
                    FilePath filePath = PathCreator.getFilePath(proPath, fileConfig, Property.LAST_PATH_O, fileName);
                    //生成文件	
                    File localFile = new File(filePath.getAllPath());
                    /**
                     * 验证文件是否已存在, 如果已存在停止该文件上传,记录上传失败原因
                     */
                    if (!fileConfig.getRenameEnabled() && localFile.exists()) {
                        result.addFailFileData(failUploadProcessor(ErrorCode.FILE_CONFLICT, fileName));
                        continue;
                    } else if (!localFile.exists()) {
                        localFile.mkdirs();
                    }
                    /**
                     * 文件传输
                     */
                        //源文件保存
                    try {
						file.transferTo(localFile);
					} catch (IllegalStateException | IOException e) {
						log.error("文件{}保存失败:{}",fileName,e.getMessage());
						throw new BaseBusinessException("文件保存失败") ;
					}
                    //执行文件转码操作
                    encoderLogWapper(filePath.getAllPath(), fileConfig);
                    //将成功的信息存入successFileData中
                    result.addSuccessFileData(getFileDo(filePath.getFileName(), file.getOriginalFilename(), filePath.getRealTivePath(), file.getSize()));

                }
            }
        }
        //如果文件需要入库，则进行文件信息入库操作
        if (Property.FILE_IN_DB) {
            fileOPInDB(result.getSuccessFileDatas());
        }
        return result;
    }


    /**
     * 文件转码日志包装（静态代理）
     * @param sourcePath
     *               文件路径
     * @param fileConfig
     *               文件类型配置
     * @throws BaseBusinessException 
     */
    private void encoderLogWapper(String sourcePath, FileTypeConfig fileConfig) throws BaseBusinessException {
        String fileType = fileConfig.getTypeName();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 开始
        doEncoder(sourcePath, fileConfig);
        stopWatch.stop();
        // 结束
        // 记录处理时间，上传监控平台
        reportToMonitorSystem(fileType, sourcePath, stopWatch.getTime());

    }

    /**
     * 记录转换所消耗时间,提交给系统监控
     *
     * @param processObjectName
     *                      处理类
     * @param source
     *             处理原文件路径
     * @param consuming
     *               处理时长（毫秒）
     */
    public void reportToMonitorSystem(String processObjectName, String source, long consuming) {
        log.info("---{}文件--->{}处理完成, 耗时: {} ms---", processObjectName, source, consuming);
    }

    /**
     * 文件处理抽象类，不同的文件类型进行不同的处理
     *
     * @param sourcePath
     *                处理文件地址
     */
    protected abstract void doEncoder(String sourcePath, FileTypeConfig fileConfig) throws BaseBusinessException;

    @Override
    public void fileOPInDB(List<FileData> successFileDatas) {
        for (FileData data : successFileDatas) {
            mapper.insert(data);
        }
        //TODO执行批量插入操作
    }

    /**
     * failUploadProcessor: 封装上传失败原因  <br/>
     *
     * @param errorCode
     *              错误码
     * @param filePath
     *              文件路径
     * @return
     * @author Lwt
     * @since JDK 1.8
     */
    private FailUploadReason failUploadProcessor(ErrorCode errorCode, String filePath) {
        FailUploadReason failUploadReason = new FailUploadReason();
        failUploadReason.setErrorCode(errorCode.getErrorCodeValue());
        failUploadReason.setFailReason(ErrorCode.FILE_CONFLICT.getCodeMessage());
        failUploadReason.setFailFileName(filePath);
        failUploadReason.setFailTime(new DateTime().toString("yyyy-MM-dd HH:mm:ss"));
        return failUploadReason;
    }

    /**
     * getFileDo: 获取文件实体 <br/>
     *
     * @param newFileName
     *                  保存文件名称
     * @param oldFileName
     *                  旧文件名称
     * @param size
     *           文件大小
     * @return
     * @author xzk
     * @Param filePath
     * @date 2018.1.23
     * @since JDK 1.8
     */
    private static FileData getFileDo(String newFileName, String oldFileName, String filePath, long size) {
        FileData fileData = new FileData();
        fileData.setId(newFileName.substring(0, newFileName.indexOf('.')));
        fileData.setFileName(oldFileName);
        fileData.setFilePath(filePath);
        fileData.setFileType(newFileName.substring(newFileName.indexOf('.') + 1));
        fileData.setFileSize(size);
        fileData.setInDate(System.currentTimeMillis());
        return fileData;
    }
}
  
	